#include <windows.h>
#include <winsock2.h>

#include "lib.h"

#include "connect.h"
#include "serv.h"
#include "myover.h"
#include "log.h"
#include "opts.h"

//For XML Translation
#include "..\common\XMLTranslation\XMLTranslator.h"
#include <vector>
#include <algorithm>
#include "XML\XMLDoc.h"

//////////////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////////////
//For XML Translation
typedef std::vector<uint8> MessageBuffer;
const char* const Connection::sXmlDeclaration = "<?xml";

enum ConnStates
{
	BADINIT, READING, WRITING, PAUSE_PENDING, PAUSED
};

Connection::Connection(Server* pServ, SOCKET client)
{
	prev = next = (Connection*)0;

	this->server = pServ;
	this->client = client;
	iused = oused = 0;
	state = READING;
	shutdownSent = false;
	reqTime = 0;

	over = new MyOverlapped(this);
	if (!over)
		state = BADINIT;

	iomutex = (PCRITICAL_SECTION)0;
	if (state != BADINIT)
	{
		iomutex = new CRITICAL_SECTION;
		if (iomutex)
			InitializeCriticalSection(iomutex);
		else
		{
			state = BADINIT;

			delete iomutex;
			iomutex = (PCRITICAL_SECTION)0;
		}
	}

	xmlTranslator = NULL;
	processingXMLMessage = false;

	// kharmon.  11-15-06.  Zero out these buffers.
	memset(ibuf, 0, sizeof(ibuf));
	memset(obuf, 0, sizeof(obuf));

	// lakers.  11-13-07.  Zero out 'history' buffers.
	// These are used to analyze crash dumps in the field.
	memset(hstbuf, 0, sizeof(hstbuf));
	memset(hstused, 0, sizeof(hstused));
	memset(hstAmt, 0, sizeof(hstAmt));
	memset(hstReadStart, 0, sizeof(hstReadStart));
	memset(hstReadAvail, 0, sizeof(hstReadAvail));
	memset(hstReadLen, 0, sizeof(hstReadLen));

	// lakers.  11-13-07.  Used to detect memory corruption
	overwriteDetector1 = OVERWRITE_1;
	overwriteDetector2 = OVERWRITE_2;
	overwriteCount = 0;

	m_sCurrency = ServerOpts::currency; // CSN-11204: return currency in Login. Default one read from server.ini
}

Connection::~Connection()
{
	closesocket();

	delete over;
	over = (MyOverlapped*)0;

	overwriteDetector1 = OVERWRITE_DEAD;
	overwriteDetector2 = OVERWRITE_DEAD;

	// Methods like closesocket() rely on iomutex, so delay its destruction
	// until the very end.
	if (iomutex)
	{
		DeleteCriticalSection(iomutex);
		delete iomutex;
		iomutex = (PCRITICAL_SECTION)0;
	}

	delete xmlTranslator;
}

bool8
Connection::initOk()
{
	return (bool8)(state != BADINIT);
}

void
Connection::closesocket()
{
	// FogBugz 314: Closing the socket is a critical section activity just
	// like reading and writing.
	lock();

	reqTime = 0;

	if (client != INVALID_SOCKET)
	{
		LINGER sockopt;

		sockopt.l_onoff = 1;
		sockopt.l_linger = 0;		// "hard" disconnect
		setsockopt(client, SOL_SOCKET,
			SO_LINGER, (char*)&sockopt, sizeof(LINGER));
		::closesocket(client);
		client = INVALID_SOCKET;
	}

	state = BADINIT;

	unlock();
}

void
Connection::lock()
{
	EnterCriticalSection(iomutex);
}

void
Connection::unlock()
{
	LeaveCriticalSection(iomutex);
}

void
Connection::ready()
{
	postRead();
}

void
Connection::close()
{
	server->close(this);
}

void
Connection::dead()
{
	server->readyForDeath(this);
}

bool8
Connection::isServerStopping()
{
	return server->isStopping();
}

char*
Connection::getServerStopMsg()
{
	shutdownSent = true;
	return server->getStopMsg();
}

void
Connection::readyToUnPause()
{
	ASSERT(state == PAUSED);

	postWrite();
}

bool8
Connection::isStale(uint32 staleAge)
{
	uint32 now = GetTickCount();
	bool8 result;

	lock();

	// NOTE: *always* allow PAUSED connections to become unpaused
	//

	result = (bool8)(state == BADINIT ||
		((state == READING || state == WRITING) &&
		now - reqTime >= staleAge));
	unlock();

	return result;
}

void
Connection::pauseResponse(bool8 pauseNow)
{
	lock();
	if (pauseNow)
	{
		// should only pause from within getResponse()
		//
		state = PAUSE_PENDING;
	}
	else					// response should be already buffered
	{
		if (state == PAUSED)
		{
			// connection is sitting idle without any posted
			// reads or writes
			//
			// This can only happen from outside of getResponse()
			// (i.e. another thread) due to critical section.
			//
			server->unpauseReq(this);
		}
		else
		{
			// Haven't gotten out of initial processIO (i.e. getResponse())
			// that put us in the paused state to begin with.
			//
			// In other words, the same thread that tried to pause has
			// changed it's mind.
			//
			state = READING;
		}
	}
	unlock();
}

void
Connection::processIO(uint32 Amt, int32 Error)
{
	lock();

	checkOverwrite();

	if (state == READING)
	{

#ifdef DEBUG
		// [AO 2008-04-02] Log Source of messages
		sockaddr_in addr;
		int len = sizeof addr;
		if (!getpeername(client, reinterpret_cast<sockaddr*>(&addr), &len))
			DEBUGMSG(("Received %d bytes from: %s", Amt, inet_ntoa(addr.sin_addr)))
		else
			DEBUGMSG(("Error in getpeername: %d", WSAGetLastError()));
#endif

		if (Error == 0 && Amt != 0)
		{
			// Save history from prior calls to ProcessIO
			for ( int h = HISTORY_DEPTH-1; h > 0; --h )
			{
				memcpy(hstbuf[h], hstbuf[h-1], sizeof(ibuf));
				hstused[h] = hstused[h-1];
				hstAmt[h] = hstAmt[h-1];
			}

			// Take a snapshot of the buffer at the start of ProcessIO
			memcpy(hstbuf[0], ibuf, sizeof(ibuf));
			hstused[0] = iused;
			hstAmt[0] = Amt;

			bool8 responseReady = false;

			iused += Amt;

			// Was any XML translated to the binary protocol?
			bool8 translatedXml = false;

			// If it's an XML message, we'll be recalculating iused...
			const int32 initialUsed = iused;

			// Figure out if this looks like an xml message
			if( !processingXMLMessage && isXmlMessage( ibuf ) )
			{
				processingXMLMessage = true;
				if (!xmlTranslator )
					allocateXMLTranslator( &xmlTranslator );
			}

			// Keep track of if/where we found the start of an incomplete XML message
			uint8* incompleteXmlLocation = NULL;
			int32  incompleteXmlSize = 0;
			if ( processingXMLMessage )
			{
				ASSERT( xmlTranslator );

				// We're going to write a new ibuf message based on the XML
				// message(s) we've just received.
				iused = 0;

				// Track how much of the xml buffer we've eaten up
				int32 xmlEat = 0;

				// ibuf may contain multiple XML messages.
				// We'll need to split them up into complete messages.
				uint8* thisMsg = ibuf;
				do
				{
					// Skip any whitespace or end-of-string characters at
					// the START of the XML message.  Always leave at least
					// one character in the buffer.
					while ( (*thisMsg == 0 || isspace(*thisMsg)) &&
						initialUsed > xmlEat + 1 )
					{
						thisMsg++;
						xmlEat++;
					}

					// Does what's left in the buffer constitute a complete XML message?
					if( isCompleteXmlMessage( thisMsg, initialUsed - xmlEat ) )
					{
						// [AO 2007-11-08] Restructuring of XML fix
						bool isXmlOK = false;

						// How many bytes did this message use?
						int32 xmlMsgUsed = 0;

						//This method, on failure, writes directly to obuf.
						//It will fail if the buffer is not valid XML.
						if ( xmlTranslator->ParseIncomingMessage( thisMsg, &xmlMsgUsed,
							obuf + oused, OUTMAX - oused ) )
						{
							xmlEat += xmlMsgUsed;

							//The 'handle' method, on success, writes directly to obuf
							//We need to track how much it writes
							int32 handleUsed = 0;

							if ( !xmlTranslator->HandleMessage( obuf + oused, OUTMAX - oused, &handleUsed ) ) 
							{
								//Translate this to the raw wire format.  For now, we'll write it
								//directly on top of the XML message, and copy it later if need be
								//to the correct location in ibuf.
								//The goal was not to change the old XML translators, but it would
								//be nice if they let us specify a place to write to...
								bool8 translatedThisXml =
									xmlTranslator->TranslateIncomingMessage( thisMsg,
									xmlMsgUsed, obuf, OUTMAX );

								if ( translatedThisXml )
								{
									// At least one binary message waiting for getReponse
									translatedXml = true;

									// How many bytes does the translated message consume?
									int32 rawMsgUsed = calculateTranslationUsed( thisMsg );

									// If we're not working on the first XML message
									if ( thisMsg != ibuf )
									{
										//Copy the translated message back to the appropriate
										//section of ibuf, so getResponse can see it.
										if ( rawMsgUsed > 0 )
										{
											memmove( ibuf + iused, thisMsg, rawMsgUsed );
										}
									}

									//And update how much of the buffer we've used
									iused += rawMsgUsed;

									// [AO 2007-11-08] Cleanup of XML fix
									isXmlOK = true;
								}
								else
								{
									DEBUGMSG(("Couldn't translate incoming message"));
								}
							}
							else
							{
								DEBUGMSG(("XMLTranslator Handled incoming message"));
							}
						}
						else
						{
							DEBUGMSG(("Couldn't parse incoming message"));
						}

						thisMsg += xmlMsgUsed;

						// [AO 2007-11-08] Restructuring of XML fix
						if (!isXmlOK)
						{
							// In this case, it was the translator's job to put error
							// stuff in obuf.  The response should be a null-terminated string.
							responseReady = true;
							oused += (strlen( (char*)(obuf + oused)) + 1);

							// kharmon.  11-07-07.  #20187.  Lynn's correction to how we recover from bad XML.
							if ( initialUsed - ( thisMsg - ibuf ) > 0 )
							{
								// More data after the invalid XML message
								incompleteXmlLocation = thisMsg;
								incompleteXmlSize = initialUsed - ( thisMsg - ibuf );
							}
							else
							{
								// No more data, reset the input buffer to empty.
								incompleteXmlSize = 0;
								incompleteXmlLocation = NULL;
								iused = 0;
							}
						}
					}
					else
					{
						//Incomplete message - get ready copy it back to the top of the ibuf,
						//set the next read location, and read more data from the client
						incompleteXmlLocation = thisMsg;
						incompleteXmlSize = initialUsed - ( thisMsg - ibuf );
					}
				}
				while( !responseReady && xmlEat < initialUsed && !incompleteXmlLocation );

				// If we do not yet have a response and there is
				// translatedXML ready to be processed then call getResponse
				// to parse the post-translated binary protocol on ibuf.
				if ( !responseReady && translatedXml )
				{
					responseReady = getResponse();
				}
			}
			else
			{
				// Always call getResponse for binary (non-XML) messages
				responseReady = getResponse();
			}

			if( incompleteXmlLocation )
			{
				//There was an incomplete XML message dangling out there.
				//Move it to the top (unless we're already there..), and reset iused;
				if( ibuf != incompleteXmlLocation && incompleteXmlSize > 0 )
				{
					// kharmon.  11-07-07.  #20187.  Lynn's correction.  Use memmove instead of memcpy, incase of
					// overlapping memory locations.
					memmove(ibuf, incompleteXmlLocation, incompleteXmlSize);
				}
				iused = incompleteXmlSize;
			}
			else if ( processingXMLMessage )
			{
				// kharmon.  11-07-07.  #20187.  Lynn's correction to how we recover from bad XML.
				// If we are processing XML and there is no incomplete
				// message in the buffer then make sure the buffer is
				// treated as empty.
				iused = 0;
			}

			// Additional sanity checking on input buffer
			if ( iused < 0 )
			{
				// This should never happen -- it would imply that we somehow
				// consumed more input than was available in the buffer.
				Log::msgError( "input buffer underflow: %d", iused );
				iused = 0;
			}
			else if ( iused >= INMAX )
			{
				// This should never happen, although a bogus client could
				// hit INMAX by never zero-terminating an XML string that
				// filled the entire input buffer.
				Log::msgError( "input buffer length exceeded: %d", iused );
				iused = 0;
			}

			if (state == READING)
			{
				if (responseReady)
				{
					// If the translatedXml flag is NOT set, it means there could
					// already be XML in the outgoing buffer, so no translation
					// is required.
					if ( oused && processingXMLMessage && translatedXml )
					{
						//Make a copy of obuf
						MessageBuffer outgoing( obuf, obuf + oused );

						//and translate the new copy back into the old buffer...
						oused = xmlTranslator->TranslateOutgoingMessage(
							obuf,
							OUTMAX,
							&outgoing[0],
							outgoing.size(),
							oused );
					}

					postWrite();
				}
				else
					postRead();		// partial input, read more from client
			}
			else if (state == PAUSE_PENDING)
			{
				state = PAUSED;
			}
			else
			{
				//
				// else paused
				//
				ASSERT(state == PAUSED);
				DEBUGMSG(("Connection::processIO: called with "
					"state == PAUSED"));
			}

			checkOverwrite();
			unlock();
		}
		else
		{
			DEBUGMSG(("Connection::processIO:  READING: %d bytes, Error: %d", Amt, Error));
			state = BADINIT;
			unlock();
			close();
		}
	}
	else if (state == WRITING)
	{
		if (Error == 0 && Amt == (DWORD)oused)
		{
			oused = 0;

			// if shutting down server, close client after shutdown msg has
			// been sent.
			//
			if (!shutdownSent)
			{
				if (moreToSend())		// fill obuf and send
				{
					if ( oused && processingXMLMessage )
					{
						//Make a copy of obuf
						MessageBuffer outgoing( obuf, obuf + oused );

						//and translate the new copy back into the old buffer...
						oused = xmlTranslator->TranslateOutgoingMessage(
							obuf,
							OUTMAX,
							&outgoing[0],
							outgoing.size(),
							oused );
					}

					postWrite();
				}
				else
					postRead();			// get next request

				checkOverwrite();
				unlock();
			}
			else
			{
				unlock();
				close();
			}
		}
		else
		{
			DEBUGMSG(("Connection::processIO:  WRITING (%ld)  ", Error));
			state = BADINIT;
			unlock();
			close();
		}
	}else if (state == PAUSE_PENDING)
			{
				state = PAUSED;
			}
	else
		unlock();
}

bool8
Connection::isXmlMessage(const uint8* const buf ) const
{
	// Only run strlen one time.
	static int nXmlDeclarationLength = strlen( sXmlDeclaration );

	// Find the first non-whitespace character
	const uint8* probe = buf;
	while ( *probe && isspace(*probe) )
		++probe;

	return 0 == memcmp( probe, sXmlDeclaration, nXmlDeclarationLength );
}

bool8
Connection::isCompleteXmlMessage(const uint8* const buf, uint32 amt ) const
{
	//We'll let it pass as a complete message if we can just find the null terminator.
	//Validity/well-formededness will be checked when we actually try to parse it out.
	return (buf + amt) != std::find( buf, buf + amt, '\0' );
}

void
Connection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = NULL;
}

//////////////////////////////////////////////////////////////////////////////
// ParseXMLLogin
//////////////////////////////////////////////////////////////////////////////
bool8 Connection::ParseXMLLogin()
{
	bool8 bRetVal = false;
	XMLDoc doc;

	// Load up the XML
	if ( doc.LoadXML( ibuf ) )
	{
		// Get the message ID
		String sMsg;
		doc.GetSingleNodeText( TEXT("//msg_id"), sMsg );

		// Check and see if that equals the Login message
		if ( sMsg == TEXT("Login") )
		{
			// Get the casino name /* TODO - What can we do with this? */
			// String sCasino;
			// doc.GetSingleNodeText( TEXT("//casino"), sCasino );

			// Get the Game ID /* TODO - What can we do with this? */
			// String sGameID;
			// doc.GetSingleNodeText( TEXT("//game_id"), sGameID );

			// kharmon.  09-06-05.  #6250.  Need to do the language setup earlier in the connection
			// process.
			// Get the language and setup the server and connection.
			String sLang;
			if ( doc.GetSingleNodeText( TEXT("//language"), sLang ) )
			{
				// set server language
				getServer()->loadStringTableForLanguage( sLang );

				// set connection language
				SetLanguage( sLang );
			}
		}
		else
		{
			// We are expecting a login message at this point,
			//  so go ahead and report and error back
			Log::msgError( "Error, expecting login message, received:\n %s.", (char *) ibuf );
		}
	}

	return bRetVal;
}

int32
Connection::calculateTranslationUsed(const uint8* const message)
{
	ASSERT( xmlTranslator );

	int32 msgUsed = 0;
	uint8 lengthBytes = xmlTranslator->GetNumBytesMsgLen();
	uint8 nextByte = 0;
	uint8 shift = 0;

	//Certain protocols use a 3-byte overhead, BUT THE 0th byte is UNUSED.
	//So account for and skip it....
	if ( lengthBytes == 3  )
	{
		ASSERT( message[nextByte] == 0 );
		--lengthBytes;
		msgUsed = 1;
		nextByte = 1;
	}

	while( nextByte < lengthBytes )
	{
		// kharmon.  11-07-07.  #20187.  Look at the current message, not the first message in the buffer.
		msgUsed += message[nextByte] << ( 8 * shift++ );
		++nextByte;
	}

	return msgUsed;
}

// [AO 2008-02-08] Return success/failure
bool Connection::postRead()
{
	WSABUF	wsabuf;
	DWORD	nRecv, flags = 0;
	DWORD	result, err;

	lock();

	ASSERT(1 == HasOverlappedIoCompleted(&over->over));

	state = READING;
	reqTime = GetTickCount();

	// Save history from prior calls
	for ( int h = HISTORY_DEPTH-1; h > 0; --h )
	{
		hstReadStart[h] = hstReadStart[h-1];
		hstReadAvail[h] = hstReadAvail[h-1];
		hstReadLen[h]   = hstReadLen[h-1];
	}

	hstReadAvail[0] = INMAX - iused;
	hstReadStart[0] = iused;
	hstReadLen[0]   = 0;

	wsabuf.len = hstReadAvail[0];				// INMAX - iused
	wsabuf.buf = (char*)ibuf + hstReadStart[0];	// (char*)ibuf + iused

	// kharmon.  11-15-06.  Zero out the memory that this recv call will write to,
	// to avoid confusion later.
	memset(wsabuf.buf, 0, wsabuf.len);

	result = WSARecv(client, &wsabuf, 1, &nRecv, &flags,
		(LPWSAOVERLAPPED)over, (LPWSAOVERLAPPED_COMPLETION_ROUTINE)0);
	err = WSAGetLastError();

	if (0 == result)
	{
		hstReadLen[0] = nRecv;

		unlock();
		return true;
	}

	if (WSA_IO_PENDING == err)
	{
		unlock();
		return true;
	}

	reqTime = 0;
	over->over.Internal = 0;
	state = BADINIT;

	unlock();

	close();
	return false;
}

// [AO 2008-02-08] Return success/failure
bool Connection::postWrite()
{
	WSABUF	wsabuf;
	DWORD	nSent;
	DWORD	result, err;

	lock();

//	ASSERT(1 == HasOverlappedIoCompleted(&over->over));

	state = WRITING;
	reqTime = GetTickCount();

	wsabuf.len = oused;
	wsabuf.buf = (char*)obuf;

	result = WSASend(client, &wsabuf, 1, &nSent, 0,
		(LPWSAOVERLAPPED)over, (LPWSAOVERLAPPED_COMPLETION_ROUTINE)0);
	err = WSAGetLastError();

	if (0 == result)
	{
		unlock();
		return true;
	}

	if (WSA_IO_PENDING == err)
	{
		unlock();
		return true;
	}

	reqTime = 0;
	over->over.Internal = 0;
	state = BADINIT;

	unlock();

	close();
	return false;
}

bool8
Connection::getResponse()
{
	if (iused)
	{
		// echo back response
		//
		CopyMemory(obuf, ibuf, iused);

		oused = iused;
		iused = 0;
	}

	return (bool8)(oused != 0);
}

void
Connection::getClientIP(String& ipaddress)
{
	char buffer[128];

	getClientIP(buffer, 128);

	ipaddress = buffer;
}

void
Connection::getClientIP(char* buf, int32 maxBufSizeIncludingNull)
{
	SOCKADDR_IN addr;
	int len = sizeof(SOCKADDR_IN);
	char* ip = (char*)0;

	if (0 == getpeername(client, (SOCKADDR*)&addr, &len))
		ip = inet_ntoa(addr.sin_addr);

	if (ip)
	{
		char* p = ip;

		while (*p++)
			/* find end */ ;

		len = (p - ip - 1);
		if (len >= maxBufSizeIncludingNull)
			len = maxBufSizeIncludingNull - 1;
		CopyMemory(buf, ip, len);
		buf[len] = '\0';
	}
	else
		*buf = '\0';
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 Connection::checkOverwrite()
///
/// This method checks for memory overwrites on the input and output
//  buffers.  There is not much we can do to recover if an overwrite is
//  detected, but we can at least log it.  An overwrite is likely to
//  eventually cause a crash, so we keep a counter for use in post-mortem.
////////////////////////////////////////////////////////////////////////////
void Connection::checkOverwrite()
{
	if ( overwriteDetector1 != OVERWRITE_1 )
	{
		++overwriteCount;
		Log::msgError( "connect.cpp -- input buffer overwrite %x", overwriteDetector1 );
		ASSERT(false);

		// reset so we can detect further overwrites
		overwriteDetector1 = OVERWRITE_1;
	}

	if (  overwriteDetector2 != OVERWRITE_2 )
	{
		++overwriteCount;
		Log::msgError( "connect.cpp -- output buffer overwrite %x", overwriteDetector2 );
		ASSERT(false);

		// reset so we can detect further overwrites
		overwriteDetector2 = OVERWRITE_2;
	}
}

WebServiceCall* Connection::wsCall()
{
	return getServer()->webServiceCall;
}
